package sokoban;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

import javax.swing.BoxLayout;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

public class Map {
	private ArrayList<MapList> listeners= new ArrayList<MapList>();
	private Grid map;
	private boolean[][] targets;
	private int playerY, pushes, moves;
	private int playerX;
	private String prevMoves;
	private Layout layout;
	private Image[][] mapGrid;
	private HighscoreList hs;
	
	private boolean canMove;
	public Map(Grid map, Layout layout){
		canMove=true;
		prevMoves="";
		this.layout=layout;
		this.map=map;
		targets=map.getTargets();
		playerX=getXpos();
		playerY=getYpos();
		pushes=0;
		moves=0;
	}
	public void addListener(MapList element){
		listeners.add(element);
	}
	public Image[][] getMapGrid(){
		return mapGrid;
	}
	public int getMoves(){
		return moves;
	}
	public int getPushes(){
		return pushes;
	}
	public boolean checkMove(Dir dir){		
		boolean ok = true;
		int dx=dir.dx;
		int dy= dir.dy;
		if(dir==Dir.UP||dir==Dir.DOWN){			
			char c=map.getGrid()[playerY+dy][playerX];
			char d=CHARS.WALL;
			if(playerY+2*dy>=0&&playerY+2*dy<map.getGrid().length){
				d=map.getGrid()[playerY+2*dy][playerX];
			}
			if((c==CHARS.BOX||c==CHARS.BOX_ON_TARGET)&&(d==CHARS.WALL||d==CHARS.BOX||d==CHARS.BOX_ON_TARGET)){
				ok=false;
			}
			if(c==CHARS.WALL){
				ok=false;
			}
		}
		else{
			char c=map.getGrid()[playerY][playerX+dx];
			char d=CHARS.WALL;
			if(playerX+2*dx>=0&&playerX+2*dx<map.getGrid()[playerY].length){
				d=map.getGrid()[playerY][playerX+2*dx];
			}
			if((c==CHARS.BOX||c==CHARS.BOX_ON_TARGET)&&(d==CHARS.WALL||d==CHARS.BOX||d==CHARS.BOX_ON_TARGET)){
				ok=false;
			}
			if(c==CHARS.WALL){
				ok=false;				
			}
		}
		return ok;
	}

	public boolean getCanMove(){
		return canMove;
	}
	public void setCanMove(boolean move){
		canMove=move;
	}
	public int getPlayerX(){
		return playerX;
	}
	public int getplayerY(){
		return playerY;
	}
	public void setPlayerX() {
		playerX=getXpos();
	}
	public void setplayerY() {
		playerY=getYpos();		
	}
	private int getXpos(){
		for(int i=0;i<map.getGrid().length;i++){
			for(int j=0;j<map.getGrid()[i].length;j++){
				if(map.getGrid()[i][j]==CHARS.PLAYER){
					return j;
				}				
			}
		}
		return -1;
	}
	private int getYpos(){
		for(int i=0;i<map.getGrid().length;i++){
			for(int j=0;j<map.getGrid()[i].length;j++){
				if(map.getGrid()[i][j]==CHARS.PLAYER){
					return i;
				}				
			}
		}
		return -1;
	}
	public boolean checkBoard(){
		boolean vinner = true;
		for(int i=0;i<map.getGrid().length;i++){
			for(int j=0;j<map.getGrid()[i].length;j++){
				if(targets[i][j]&&!(map.getGrid()[i][j]==CHARS.BOX_ON_TARGET)){
					vinner=false;
				}
			}
		}
		return vinner;
	}
	
	private void print(){
		for(int i=0;i<map.getGrid().length;i++){
			for(int j=0;j<map.getGrid()[i].length;j++){
				
				System.out.print(map.getGrid()[i][j]);
			}
			System.out.print("\n");
		}		
	}

	private void doPush(Dir dir){		
		int dx=dir.dx;
		int dy=dir.dy;
		if(targets[playerY+2*dy][playerX+2*dx]){
			setSign(CHARS.BOX_ON_TARGET,2*dx,2*dy);
		}
		else{
			setSign(CHARS.BOX,2*dx,2*dy);
		}
	}
	public void undoMove(){
		if(prevMoves.length()>0){
			char temp = prevMoves.charAt(prevMoves.length()-1);		
			prevMoves=(String) prevMoves.subSequence(0,prevMoves.length()-1);
			doMove(Dir.getDir(temp).invertDir(),true);
			if(Character.isUpperCase(temp)){
				pushes--;
				doDrag(Dir.getDir(temp));					
			}
			else{
				moves--;
			}
	    	printMap();
				
				
		}
	}

	private void doDrag(Dir dir){
		
		if(targets[playerY+2*dir.dy][playerX+2*dir.dx]){
			setSign(CHARS.TARGET,2*dir.dx,2*dir.dy);
					}
		else{
			setSign(CHARS.EMPTY,2*dir.dx,2*dir.dy);

		}
		if(targets[playerY+dir.dy][playerX+dir.dx]){
			setSign(CHARS.BOX_ON_TARGET,dir.dx,dir.dy);
		}
		else{
			setSign(CHARS.BOX,dir.dx,dir.dy);

		}	
	}

	public void doMove(Dir dir,boolean undo){		
		if(canMove){
			int dx= dir.dx;
			int dy= dir.dy;
			char c=getSign(dx,dy);
			if(!undo){
				if(c==CHARS.BOX||c==CHARS.BOX_ON_TARGET){			
					prevMoves+=Character.toUpperCase(dir.getSymb());
					doPush(dir);
					pushes++;
				}	
				else{		
					moves++;	

					prevMoves+=dir.getSymb();						
				}
			}
			
			if(targets[playerY+dy][playerX+dx]){
				setSign(CHARS.PLAYER_ON_TARGET,dx,dy);
			}
			else{
				setSign(CHARS.PLAYER,dx,dy);
			}
			if(targets[playerY][playerX]){
				setSign(CHARS.TARGET);
			}
			else{
				setSign(CHARS.EMPTY);
			}
			playerX+=dx;
			playerY+=dy;			
		}	
		printMap();

	}

	public void checkInput(KeyEvent e){
		int keyCode=e.getKeyCode();
		Dir qq = null;
		boolean dirKey=true;
		if(canMove){
			switch(keyCode){
				case KeyEvent.VK_UP:
					qq=Dir.UP;
					break;
				case KeyEvent.VK_DOWN:
					qq=Dir.DOWN;
					break;
				case KeyEvent.VK_RIGHT:
					qq=Dir.RIGHT;	
					break;
				case KeyEvent.VK_LEFT:
					qq=Dir.LEFT;
					break;
				default:
					dirKey=false;
			}
			if(dirKey){
				if(checkMove(qq)){
					doMove(qq, false);
				}
			}
			else{
				checkInput(e.getKeyChar());
			}
		}
		if(checkBoard()){
			canMove=false;
			end();
		}
	}
	public void checkInput(char ch){	
		 if(canMove){
			switch(ch){
				
				case 'r':
					undoMove();
					break;	
				case 'h':
					
			}
			
		}
		else{
			switch(ch){
				case 'y':
					if(!canMove){
						new Sokoban();
					}
					break;
				default:
					System.exit(0);
			}
		}
	}

	public void setSign(char c, int xOffset, int yOffset){
		fireListChanged(c, playerX+xOffset, playerY+yOffset);
	}

	public void setSign(char c){
		fireListChanged(c,playerX,playerY);
	}
	
	public char getSign(){
		return map.getSign(playerX, playerY);
	}
	public char getSign(int xOffset, int yOffset){
		return map.getSign(playerX+xOffset,playerY+yOffset);
	}

	


	
	public void printMap() {
		System.out.println(map.getLevelName());
		layout.setGameLabels(moves, pushes, prevMoves);
		print();
		System.out.println("Moves: " + moves+" Pushes: "+ pushes);
		System.out.println(prevMoves);
	}
	
	public void end() {
		layout.end();
		
	}
	public void writeHs(){
		
		
		if(hs.isHs((getPushes()+getMoves()))){
			JDialog dialog = new JDialog(layout.getFrame(),"Highscores for "+map.getLevelName());

			hs.addResult(new SokobanResult((String)JOptionPane.showInputDialog(
					dialog ,
					"Hva er ditt navn? ",
                	"NY HIGHSCORE!",
                	JOptionPane.PLAIN_MESSAGE,
                	null,
                	null,
                	null),(getMoves()+(getPushes()))));
				BufferedWriter buff = null;
		
			try {
				System.out.println(hs.geturl());
				buff=new BufferedWriter(new FileWriter(hs.geturl()));
				buff.write("HS");
				buff.newLine();
				for(SokobanResult res:hs){
					buff.write(res.getName()+";"+res.getMoveCount());
						buff.newLine();
				}
		
			} catch (IOException e) {
				System.out.println("ooj");
				e.printStackTrace();
			}finally{
				try{
					if(buff!=null){
						buff.flush();
						buff.close();
					}
				
				}catch(IOException qqq){
					qqq.printStackTrace();
				}
			}
		}
	}
	public void setHs(HighscoreList hs) {
		this.hs=hs;
	}
	public void printHs(){
		JDialog dialog = new JDialog(layout.getFrame(),"Highscores for "+map.getLevelName());
		
		
		JPanel qq= new JPanel();
		qq.setPreferredSize(new Dimension(300,500));
		qq.setLayout(new BoxLayout(qq,BoxLayout.Y_AXIS));
		JPanel overskrift= new JPanel();
		JPanel scores  = new JPanel();
		overskrift.setBackground(Color.black);
		scores.setBackground(Color.blue);
		
		overskrift.setLayout(new GridLayout(0,2));
		overskrift.add(new JLabel("NAME"));
		overskrift.add(new JLabel("SCORE"));
		scores.setPreferredSize(new Dimension(200,300));
		scores.setLayout(new GridLayout(10,2));
		int count=0;
		if(hs.size()==0){
			scores.add(new JLabel("Ingen Highscores enda"));
		}
		else{
		for(SokobanResult res: hs){
			if(count<10){
				JLabel tempName= new JLabel(res.getName());
				JLabel tempScore = new JLabel(res.getMoveCount()+"");
				tempName.setAlignmentX(Component.LEFT_ALIGNMENT);
				tempScore.setAlignmentX(Component.RIGHT_ALIGNMENT);
				scores.add(tempName);
				scores.add(tempScore);
			}
			else{
				break;
			}
			count++;
		}
		}
		qq.add(overskrift);
		qq.add(scores);
		dialog.add(qq);
		dialog.setMinimumSize(new Dimension(300,500));
		dialog.setLocationRelativeTo(null);
		dialog.setVisible(true);
	}
	private void fireListChanged(char c, int x, int y){
		for(MapList element: listeners){
			element.updateImages(c, x, y);
		}
	}
}
